package com.piece.admin.service;

import com.piece.admin.model.TbSysDept;
import com.piece.admin.model.TbSysRole;
import com.piece.admin.repository.TbSysDeptRepository;
import com.piece.admin.repository.TbSysUserRepository;
import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.enums.sql.SqlKey;
import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.exception.FrameWorkException;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import com.piece.core.jpa.repository.BaseService;
import com.piece.core.framework.support.bootstrap.Order;
import com.piece.core.framework.support.bootstrap.TreeData;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.object.ClassUtil;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.*;

/**
 * 部门 业务层处理
 */
@Service
@Transactional
public class TbSysDeptService extends BaseService<TbSysDept, String> {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Autowired
    private TbSysUserRepository tbSysUserRepository;

    @Autowired
    private TbSysDeptRepository tbSysDeptRepository;

    @PostConstruct
    public void initRepository() {
        setRepository(tbSysDeptRepository);
    }

    /**
     * 新增部门信息
     */
    @Override
    public TbSysDept insert(TbSysDept dept) {
        TbSysDept info = tbSysDeptRepository.findById(dept.getParentId());
        // 如果父节点不为"正常"状态,则不允许新增子节点
        if (!StatusType.NORMAL.getCode().equals(info.getStatus())) {
            throw new ServerException(I18nUtil.message(ExceptionConstants.NO_PARENT, info.getDeptName()));
        }
        dept.setAncestors(info.getAncestors() + "," + dept.getParentId());
        return tbSysDeptRepository.insert(dept);
    }

    /**
     * 修改部门信息
     */
    @Override
    public void update(TbSysDept dept) {
        TbSysDept info = tbSysDeptRepository.findById(dept.getParentId());
        if (null != info) {
            String ancestors = info.getAncestors() + "," + info.getDeptId();
            dept.setAncestors(ancestors);
            updateDeptChildren(dept.getDeptId(), ancestors);
        }
        tbSysDeptRepository.update(dept);
        if (StatusType.NORMAL.getCode().equals(dept.getStatus())) {
            // 如果该部门是启用状态，则启用该部门的所有上级部门
            updateDeptParent(dept);
        }
    }

    /**
     * 修改该部门的父级部门状态
     */
    private void updateDeptParent(TbSysDept dept) {
        String updateBy = dept.getUpdateBy();
        dept = tbSysDeptRepository.findById(dept.getParentId());
        if (null != dept) {
            dept.setUpdateBy(updateBy);
            dept.setStatus(StatusType.NORMAL.getCode());
            tbSysDeptRepository.update(dept);
            updateDeptParent(dept);
        }
    }

    /**
     * 修改子部门关系
     *
     * @param deptId    部门ID
     * @param ancestors 祖级列表
     */
    public void updateDeptChildren(Long deptId, String ancestors) {
        HashMap<String, Object> condition = new HashMap<>();
        condition.put("parentId", deptId);
        condition.put("status", StatusType.NORMAL.getCode());
        List<TbSysDept> children = tbSysDeptRepository.findByCondition(condition, null);
        for (TbSysDept child : children) {
            child.setAncestors(ancestors + "," + deptId);
        }
        if (children.size() > 0) {
            tbSysDeptRepository.batchUpdate(children);
        }
    }

    /**
     * 删除部门
     */
    @Override
    public void delete(Serializable deptId) {
        tbSysDeptRepository.updateByProperties("deptId", (Long) deptId, "status", StatusType.DELETE.getCode());
    }

    /**
     * 批量删除部门信息
     */
    @Override
    public int delete(Serializable[] ids) {
        List<Long> param_in = Arrays.asList((Long[]) ids);
        return tbSysDeptRepository.updateByProperties("Q_deptId_L_IN", param_in, "status", StatusType.DELETE.getCode());
    }

    /**
     * 根据父节点的ID获取所有子节点
     */
    public List<TbSysDept> findChildTable(List<TbSysDept> list, long parentId) {
        List<TbSysDept> returnList = new ArrayList<>();
        for (Iterator<TbSysDept> iterator = list.iterator(); iterator.hasNext(); ) {
            TbSysDept dept = iterator.next();
            // 根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (dept.getParentId() == parentId) {
                recursionFn(list, dept);
                returnList.add(dept);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<TbSysDept> list, TbSysDept dept) {
        // 得到子节点列表
        List<TbSysDept> childList = getChildList(list, dept);
        dept.setChildren(childList);
        for (TbSysDept tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                Iterator<TbSysDept> it = childList.iterator();
                while (it.hasNext()) {
                    TbSysDept n = it.next();
                    recursionFn(list, n);
                }
            }
        }
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<TbSysDept> list, TbSysDept dept) {
        return getChildList(list, dept).size() > 0;
    }

    /**
     * 获取子节点列表
     */
    private List<TbSysDept> getChildList(List<TbSysDept> list, TbSysDept dept) {
        List<TbSysDept> clist = new ArrayList<>();
        Iterator<TbSysDept> it = list.iterator();
        while (it.hasNext()) {
            TbSysDept n = it.next();
            if (n.getParentId().equals(dept.getDeptId())) {
                clist.add(n);
            }
        }
        return clist;
    }

    public List<Long> getDeptCondition(Long parentId) {
        TbSysDept dept = findById(parentId);
        if (null != dept) {
            List<Long> deptIds = jdbcTemplate.queryForList("select dept_id from `tb_sys_dept` where dept_id = ? or ancestors like ?",
                    Long.TYPE, parentId, dept.getAncestors() + FrameWorkConstants.SPLIT_PARAM + parentId + "%");
            return deptIds;
        }
        return null;
    }

    /**
     * 查询部门树
     */
    public List<TreeData> findTreeData() {
        List<TbSysDept> deptList = findAll();
        return findTreeData(deptList, false, null);
    }

    /**
     * 对象转部门树
     *
     * @param deptList     部门列表
     * @param isCheck      是否需要选中
     * @param roleDeptList 角色已存在部门列表
     * @return
     */
    public List<TreeData> findTreeData(List<TbSysDept> deptList, boolean isCheck, List<String> roleDeptList) {
        List<TreeData> trees = new ArrayList<>();
        for (TbSysDept dept : deptList) {
            TreeData treeData = TreeData.builder()
                    .id(String.valueOf(dept.getDeptId()))
                    .pId(String.valueOf(dept.getParentId()))
                    .title(dept.getDeptName())
                    .name(dept.getDeptName())
                    .build();

            if (isCheck) {
                treeData.setChecked(roleDeptList.contains(dept.getDeptId().toString()));
            } else {
                treeData.setChecked(false);
            }
            trees.add(treeData);
        }
        return trees;
    }

    /**
     * 根据角色ID查询部门（数据权限）
     *
     * @param role 角色对象
     * @return 部门列表（数据权限）
     */
    public List<TreeData> findTreeData(TbSysRole role) {
        Long roleId = role.getRoleId();
        List<TreeData> trees;
        List<TbSysDept> deptList = findAll();
        if (ClassUtil.isEmpty(roleId)) {
            String sql = "select d.dept_id as dept_name " +
                    "from sys_dept d" +
                    "left join sys_role_dept rd on d.dept_id = rd.dept_id " +
                    "where d.del_flag = '0' and rd.role_id = " + roleId + " order by d.parent_id, d.order_num";
            List<String> roleDeptList = jdbcTemplate.queryForList(sql, String.class);
            trees = findTreeData(deptList, true, roleDeptList);
        } else {
            trees = findTreeData(deptList, false, null);
        }
        return trees;
    }

    /**
     * 查询子部门数量
     *
     * @param parentId 部门ID
     * @return 结果
     */
    public int findChildDeptCount(Long parentId) {
        String hql = "from TbSysDept where parentId = ?0 and status = ?1 ";
        return tbSysDeptRepository.count(hql, new Object[]{parentId, StatusType.NORMAL.getCode()}, true).intValue();
    }

    /**
     * 根据父节点的ID获取所有子节点
     */
    public TbSysDept findParentTable(TbSysDept dept) {
        TbSysDept parentDept = null;
        TbSysDept childDept = null;
        Long[] ancestors = Convert.toLongArray(",", dept.getAncestors() + "," + dept.getDeptId());
        List<Long> deptIds = Arrays.asList(ancestors);
        List<TbSysDept> deptList = tbSysDeptRepository.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("Q_deptId_L_IN", deptIds)
                .put("status", StatusType.NORMAL.getCode())
                .build(), null);
        deptList.sort(new Comparator<TbSysDept>() {
            @Override
            public int compare(TbSysDept child, TbSysDept parent) {
                return parent.getAncestors().length() - child.getAncestors().length();
            }
        });

        for (TbSysDept tempDept: deptList) {
            if (null == childDept) {
                childDept = tempDept;
            }
            if (null != parentDept) {
                parentDept = tempDept;
                parentDept.setChildren(Arrays.asList(childDept));
                childDept = parentDept;
            } else {
                parentDept = childDept;
            }
        }

        return parentDept;
    }

    /**
     * 查询上级部门/机构
     *
     * @param deptId 当前部门ID
     * @param isOrg  是否查到机构
     * @return
     */
    public TbSysDept findParentDept(Long deptId, boolean isOrg) {
        TbSysDept dept = findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("deptId", deptId)
                .put("status", StatusType.NORMAL.getCode())
                .build());
        if (null != dept && dept.getParentId() != 0) {
            if (!isOrg) {
                dept = findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                        .put("deptId", dept.getParentId())
                        .put("status", StatusType.NORMAL.getCode())
                        .build());
            } else {
                Long[] ancestors = Convert.toLongArray(",", dept.getAncestors());
                List<TbSysDept> deptList = tbSysDeptRepository.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                        .put("Q_deptId_L_IN", Arrays.asList(ancestors))
                        .put("status", StatusType.NORMAL.getCode())
                        .build(), null);
                deptList.sort(new Comparator<TbSysDept>() {
                    @Override
                    public int compare(TbSysDept child, TbSysDept parent) {
                        return child.getAncestors().length() - parent.getAncestors().length();
                    }
                });

                for (TbSysDept tempDept: deptList) {
                    if (StatusType.NORMAL.getCode().equals(tempDept.getType())) {
                        return tempDept;
                    }
                    dept = tempDept;
                }
            }
        }

        return dept;
    }

    /**
     * 查询部门是否存在用户
     *
     * @param deptId 部门ID
     * @return 结果 true 存在 false 不存在
     */
    public boolean checkDeptExistUser(Long deptId) {
        String hql = "from TbSysUser where deptId = ?0 and status = ?1";
        long count = tbSysUserRepository.count(hql, new Object[]{deptId, StatusType.NORMAL.getCode()}, true);
        return count > 0;
    }

    /**
     * 校验部门名称是否唯一
     *
     * @param deptName 部门名称
     */
    public boolean checkDeptNameUnique(String deptName) {
        String hql = "from TbSysDept where deptName = ?0 and status = ?1";
        long count = tbSysDeptRepository.count(hql, new Object[]{deptName, StatusType.NORMAL.getCode()}, true);
        return count <= 0;
    }


    public List<String> findDeptNames(Long[] ids) {
        String sql = "select dept_name from tb_sys_dept where dept_id in(";
        for (Long id : ids) {
            sql += "?,";
        }
        sql = sql.substring(0, sql.length() - 1) + ")";
        return jdbcTemplate.queryForList(sql, ids, String.class);
    }

    public String convertIdToName(Long id) {
        String sql = "select dept_name from tb_sys_dept where dept_id =:deptId";
        Map<String, Object> param = new HashMap<>();
        param.put("deptId", id);
        return namedParameterJdbcTemplate.queryForObject(sql, param, String.class);
    }

    public String convertIdToNames(String ids) {
        String sql = "select dept_name from tb_sys_dept where dept_id in (:deptIds)";
        Map<String, Object> param = new HashMap<>();
        param.put("deptIds", Convert.toLongList(ids));
        List<String> names = namedParameterJdbcTemplate.queryForList(sql, param, String.class);
        if (names.size() > 0) {
            return StringUtil.join(names, FrameWorkConstants.SPLIT_PARAM);
        }
        return "";
    }

    /**
     * 根据角色ID查询数据权限
     *
     * @param roleId 角色对象
     */
    public String findDeptIdByRole(Long roleId) {
        String sql = "select d.dept_id as dept_name " +
                "from tb_sys_dept d " +
                "left join tb_sys_role_dept rd on d.dept_id = rd.dept_id " +
                "where d.status = '0' and rd.role_id = " + roleId + " order by d.parent_id, d.order_num";
        List<String> roleDeptList = jdbcTemplate.queryForList(sql, String.class);
        if (null == roleDeptList || roleDeptList.size() == 0) {
            return null;
        }
        return StringUtil.join(roleDeptList, ",");
    }

    public void validDept(Collection<Long> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return;
        }

        List<TbSysDept> depts = tbSysDeptRepository.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("deptId", new ArrayList<>(ids))
                .build(), null);
        Map<Long, TbSysDept> deptMap = CollectionUtil.convertMap(depts, TbSysDept::getDeptId);
        ids.forEach(id -> {
            TbSysDept dept = deptMap.get(id);
            if (null == dept) {
                throw new FrameWorkException(new ErrorCode(ExceptionConstants.NOT_EXIST, "部门【" + id + "】"));
            }
            if (StatusType.NORMAL.getCode().equals(dept.getStatus())) {
                throw new FrameWorkException(new ErrorCode(ExceptionConstants.IS_DISABLE, dept.getDeptName()));
            }
        });
    }

    @Override
    protected List<Order> generateDefaultOrder() {
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("parentId", SqlKey.ASC));
        orders.add(new Order("orderNum", SqlKey.ASC));
        return orders;
    }
}
